#include "nfa.hpp"

namespace nfa
{
    class NfaHandler
    {
    public:
        NfaHandler ():_alloc_cnt(0), _alloc_num(0) 
        { for (size_t i = 0; i < BUF_SIZE; ++i) _nfa_buffer[i] = new Nfa(); }

        static void showNfa(const NfaPair* nfapair) 
        {
            Nfa* start = nfapair->_start;
            std::stack<Nfa*> st;
            std::unordered_set<Nfa*> visited;
            if (start) {
                st.push(start);
                while (!st.empty()) {
                    auto t = st.top();
                    st.pop();
                    visited.insert(t);
                    if (t->_next1) {
                        std::cout << "Nfa: " << t->_num << " -> " << t->_next1->_num << " " << mapRecv[t->getEdge()];
                        if (t->getEdge() == Recv::CCL) std::cout << " " << t->getSetStr();
                        std::cout << std::endl;
                        if (visited.find(t->_next1) == visited.end()) st.push(t->_next1);
                    }
                    if (t->_next2) {
                        std::cout << "Nfa: " << t->_num << " -> " << t->_next2->_num << " " << mapRecv[t->getEdge()];
                        if (t->getEdge() == Recv::CCL) std::cout << " " << t->getSetStr();
                        std::cout << std::endl;
                        if (visited.find(t->_next2) == visited.end()) st.push(t->_next2);
                    }
                }
            }
        }

        Nfa* newNfa()
        {
            Nfa* res = nullptr;
            if (!_nfa_st.empty()) {
                res = _nfa_st.top();
                _nfa_st.pop();
            } else {
                if (_alloc_cnt >= BUF_SIZE) {
                    log::logMessage(log::LOG_LEVEL::_FATAL, "alloc error, no more source");
                    exit(1);
                } else {
                    res = _nfa_buffer[_alloc_cnt++];
                }
            }
            res->setNum(++_alloc_num);
            return res;
        }

        void discardNfa(Nfa* nfa)
        {
            assert(nfa);
            nfa->clearState();
            --_alloc_num;
            _nfa_st.push(nfa);
        }

        size_t getAllocNum()
        { return _alloc_num; }

        virtual ~NfaHandler () 
        {
            for (auto& e : _nfa_buffer) free(e);
            while (!_nfa_st.empty()) {
                delete _nfa_st.top();
                _nfa_st.pop();
            }
        }
    
    private:
        nfa::Nfa* _nfa_buffer[BUF_SIZE];
        std::stack<Nfa*> _nfa_st;
        size_t _alloc_cnt;
        size_t _alloc_num;
    };
    
} /* namespace nfa */
